package com.gite.nouette.admin.implementation.service;

import java.util.Collection;
import java.util.Map;

import com.gite.nouette.admin.contract.data.IData;
import com.gite.nouette.admin.contract.data.IJsonRpcRequest;
import com.gite.nouette.admin.contract.data.IJsonRpcResponse;
import com.gite.nouette.admin.contract.data.ILog;
import com.gite.nouette.admin.contract.service.IVisitorData;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONBoolean;
import com.google.gwt.json.client.JSONNull;
import com.google.gwt.json.client.JSONNumber;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.json.client.JSONValue;

public class DataToJson implements IVisitorData {
	private JSONValue current;

	public <V> JSONValue getJsonValue(V value) {
		if (value == null) {
			setCurrent(JSONNull.getInstance());
		} else if (value instanceof Boolean) {
			Boolean bool = (Boolean) value;
			setCurrent(JSONBoolean.getInstance(bool));
		} else if (value instanceof String) {
			String str = (String) value;
			setCurrent(new JSONString(str));
		} else if (value instanceof Number) {
			Number number = (Number) value;
			setCurrent(new JSONNumber(number.doubleValue()));
		} else if (value instanceof Collection<?>) {
			Collection<?> collection = (Collection<?>) value;
			collectionValueToJsonArray(collection);
		} else if (value instanceof Map<?, ?>) {
			Map<?, ?> map = (Map<?, ?>) value;
			mapValueToJsonObject(map);
		} else if (value instanceof IData) {
			IData data = (IData) value;
			data.accept(this);
		} else {
			throw new IllegalArgumentException("value of type(=" + value.getClass() + ") is not supported");
		}

		return getCurrent();
	}

	private <V> void collectionValueToJsonArray(Collection<V> values) {
		JSONArray jsonArray = new JSONArray();
		int i = 0;
		for (V value : values) {
			jsonArray.set(i, getJsonValue(value));
			i++;
		}

		setCurrent(jsonArray);
	}

	private <K, V> void mapValueToJsonObject(Map<K, V> values) {
		JSONObject jsonObject = new JSONObject();
		for (Map.Entry<K, V> entry : values.entrySet()) {
			String key = (String) entry.getKey();
			JSONValue value = getJsonValue(entry.getValue());
			jsonObject.put(key, value);
		}

		setCurrent(jsonObject);
	}

	@Override
	public void visitJsonRpcRequest(IJsonRpcRequest jsonRpcRequest) {
		JSONObject jsonObject = new JSONObject();
		jsonObject.put("jsonrpc", getJsonValue(jsonRpcRequest.getJsonrpc()));
		jsonObject.put("method", getJsonValue(jsonRpcRequest.getMethod()));
		jsonObject.put("params", getJsonValue(jsonRpcRequest.getParams()));
		jsonObject.put("id", getJsonValue(jsonRpcRequest.getId()));

		setCurrent(jsonObject);
	}

	@Override
	public void visitJsonRpcResponse(IJsonRpcResponse<?> jsonRpcResponse) {
		JSONObject jsonObject = new JSONObject();
		jsonObject.put("jsonrpc", getJsonValue(jsonRpcResponse.getJsonrpc()));
		jsonObject.put("result", getJsonValue(jsonRpcResponse.getResult()));
		jsonObject.put("error", getJsonValue(jsonRpcResponse.getError()));
		jsonObject.put("id", getJsonValue(jsonRpcResponse.getId()));

		setCurrent(jsonObject);
	}

	@Override
	public void visitLog(ILog log) {
		JSONObject jsonObject = new JSONObject();
		jsonObject.put("message", getJsonValue(log.getMessage()));

		setCurrent(jsonObject);
	}

	/* GETTERS & SETTERS */

	private JSONValue getCurrent() {
		return current;
	}

	private void setCurrent(JSONValue current) {
		this.current = current;
	}

}
